precision mediump float;

struct directional_light {
	vec3 position;			
	vec3 halfplane;			
	vec4 ambient_color;  // make global
	vec4 diffuse_color;
	vec4 specular_color;
};

struct spot_light {
	vec3 position;
	vec3 direction;
	vec3 halfplane;  // delete this one
	float exponent;
	float angle;
	vec4 ambient_color;  // make global
	vec4 diffuse_color;
	vec4 specular_color;
};

struct material_properties {
	vec4 ambient_color;
	vec4 diffuse_color;
	vec4 specular_color;
	float specular_exponent;
};


varying vec3 v_normal;
varying vec4 v_color;
varying vec2 v_tc;
varying vec3 v_position;
varying float v_cel_intensity;

varying vec4 v_shadow_coord;
uniform int u_use_spotlights;

uniform float v_lightingMode;
uniform float v_mappingMode;

uniform sampler2D sTexture;
uniform sampler2D shadowTexture;
uniform sampler2D normalMap;

uniform material_properties v_material;
//uniform directional_light v_light;
uniform spot_light v_light;
uniform spot_light v_light1;
uniform spot_light v_light2;
uniform spot_light v_light3;
uniform spot_light v_light4;
uniform spot_light v_light5;
uniform spot_light v_light6;
uniform spot_light v_light7;


uniform int v_light_num; 

uniform vec4 v_global_ambient_color;

const float c_null = 0.0;
const float c_one = 1.0;
const float omega = 2.5;

vec4 computed_color;
vec3 map_normal;
vec3 normal;

//+++DEPACK DEPTH VALUE+++//

float getShadowFactor(vec4 lightZ){
	
	vec4 packedZValue = texture2D(shadowTexture, lightZ.xy);
	
	const vec4 bitShifts = vec4(1.0 / (256.0 * 256.0 * 256.0), 
								1.0 / (256.0 * 256.0),
								1.0 / 256.0,
								1.0);
	float shadow = dot(packedZValue, bitShifts);
	
	
	return float(shadow >= lightZ.z);
}



void main() {



//+++MAPPING CALCULATION+++//

if(v_mappingMode == 0.0){
	normal = v_normal;
}

else if(v_mappingMode == 1.0){
	map_normal = texture2D(normalMap, v_tc).rgb;
	normal = (map_normal * 2.0) - 1.0;
}
else if(v_mappingMode == 2.0){
	float h = texture2D(normalMap, v_tc.st).a;
	vec3 xPos = vec3(-v_position.x, 0.0, -v_position.z);
	vec3 yPos = vec3(0.0, -v_position.y, -v_position.z);
	float deltaU = tan(acos(dot(xPos, v_normal))) * h;
	float deltaV = tan(acos(dot(yPos, v_normal))) * h;
	vec2 delta = vec2(0.0, 0.0);
	delta.s = v_tc.s + deltaU;
	delta.t = v_tc.t + deltaV;
	
	map_normal = texture2D(normalMap, delta).rgb;
	normal = (map_normal * 2.0) - 1.0;
}


//+++SHADOW CALCULATION+++//

float sValue = 1.0;

if (v_shadow_coord.w > 0.0){

	//shadow coordinates for texture look-up
	vec4 lightZ = v_shadow_coord / v_shadow_coord.w;
	lightZ = (lightZ + 1.0) / 2.0;
	lightZ += 0.0005;
	
//float distance = texture2D(shadowTexture, lightZ.xy).a;
	 
	// get the shadow value
	//if(distance < lightZ.r){
		//sValue = 0.0;
		//}
	sValue = getShadowFactor(lightZ);
	}

//+++LIGHT CALCULATION+++//


// Gouraud-lighting
if(v_lightingMode == 1.0){

	vec4 tex_color = texture2D(sTexture, v_tc);

	computed_color = v_color * tex_color;
	}

//Phong-lighting
else if(v_lightingMode == 2.0){

	//TODO: Calculation of real_halfplane


	vec3 norm_normal = normalize(normal);
		
	vec4 tex_color = texture2D(sTexture, v_tc);

	vec4 light_color;
	
	vec3 norm_lightvector = normalize(v_position - v_light.position);
	float spot_factor = 1.0;


//Spotlight Faktor
//determines if the Vertex is in the Spotlight cone.

	if(u_use_spotlights == 1){
	
		vec3 norm_lightdirection = normalize(-v_light.direction);
		
		if(v_light.angle < 180.0){
		
			float DdotV = dot(norm_lightdirection, norm_lightvector);
				
			if(DdotV >= cos(radians(v_light.angle)))
				spot_factor = pow(DdotV, v_light.exponent) + 0.2;
			else
				spot_factor = 0.2;
			}
		}
		
	// Dot-products
	float LdotN = dot(norm_lightvector, norm_normal);
	float HdotN = dot(norm_normal, v_light.halfplane);
	float VdotN = dot(norm_normal, v_position);
	
	// Oren-Nayar Variables
	float omgeasquared = pow(omega, 2.0);
	float A = 1.0 - (0.5 * omgeasquared) / (omgeasquared + 0.33);
	float B = (0.45 * omgeasquared) / (omgeasquared + 0.09);
	float Irradiance = max(0.0, LdotN);
	float AngleViewNormal = acos(VdotN);
	float AngleLightNormal = acos(LdotN);
	float AngleDifference = max(0.0, dot(normalize(v_position - norm_normal * VdotN), normalize(v_light.position - norm_normal * LdotN)));
	float alpha = max(AngleViewNormal, AngleLightNormal);
	float beta = min(AngleViewNormal, AngleLightNormal);
	
	// Term-calculations

	vec4 diffuse_term = Irradiance * (v_light.diffuse_color * v_material.diffuse_color); 
	vec4 diffuse_ref_term = diffuse_term * (A + B * AngleDifference * sin(alpha) * tan(beta));
	
	vec4 specular_term = pow(max(0.0, HdotN), v_material.specular_exponent) * (v_light.specular_color * v_material.specular_color);
	
///////////////////////////////////////////////////////////////
// ### switch option here ###
// 'diffuse_term' for simple smooth diffuse lighting
// 'diffuse_ref_term' for rough lighting depending on 'omega'
///////////////////////////////////////////////////////////////
	

	computed_color = (diffuse_term + specular_term) * spot_factor;
								

	if(tex_color.a == 1.0){
		computed_color += (v_global_ambient_color * v_material.ambient_color);
		}
		
	computed_color *= tex_color;
	
	
	}
	
// Cel-shading  +++ Does not go along with Normal Mapping +++
else if(v_lightingMode == 3.0){

	vec4 intensity;

	vec3 norm_normal = normalize(v_normal);
	vec3 norm_lightvector = normalize(v_light.position);
	
	float LdotN = dot(norm_lightvector, norm_normal);
	vec4 tex_color = texture2D(sTexture, v_tc);

	if (LdotN > 0.95)
		intensity = vec4(0.95, 0.95, 0.95, 1.0);
	else if (LdotN > 0.5)
		intensity = vec4(0.5, 0.5, 0.5, 1.0);
	else if (LdotN > 0.35)
		intensity = vec4(0.35, 0.35, 0.35, 1.0);
	else
		intensity = vec4(0.15, 0.15, 0.15, 1.0);
	computed_color = intensity * v_material.diffuse_color * v_light.diffuse_color * tex_color;

	
	//Edge detection in post-processing
	}

vec3 computed_shadow_color = computed_color.rgb * sValue;
gl_FragColor = vec4(computed_shadow_color, computed_color.a);
}